package com.lanchetech.merchant.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.dto.SnapshotDTO;
import com.lanchetech.bean.request.*;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.response.SpuResp;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.*;
import com.lanchetech.common.enums.*;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.merchant.service.ProductService;
import com.lanchetech.service.DaoService;
import com.lanchetech.service.OSSClientUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lanchetech.common.constants.JypConstant.MAX_AMOUNT;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    SpuMapper spuMapper;
    @Autowired
    SpecMapper specMapper;
    @Autowired
    CategorySpecMapper categorySpecMapper;
    @Autowired
    UnitMapper unitMapper;
    @Autowired
    FreightMapper freightMapper;
    @Autowired
    SkuMapper skuMapper;
    @Autowired
    SpuAttributeMapper spuAttributeMapper;
    @Autowired
    CategoryMapper categoryMapper;
    @Autowired
    FreightListMapper freightListMapper;
    @Autowired
    SnapshotMapper snapshotMapper;
    @Autowired
    BrandMapper brandMapper;
    @Autowired
    DaoService daoService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    CommentStatisticsMapper commentStatisticsMapper;
    @Autowired
    SpuCommissionMapper spuCommissionMapper;
    @Autowired
    ShopMapper shopMapper;
    @Autowired
    AppointmentSettingMapper appointmentSettingMapper;
    @Autowired
    TenantMapper tenantMapper;
    @Autowired
    OrdersMapper ordersMapper;
    @Autowired
    OrderSkuMapper orderSkuMapper;
    @Autowired
    AfterSaleMapper afterSaleMapper;
    @Autowired
    ReplaceImageMapper replaceImageMapper;
    @Autowired
    ZoneMapper zoneMapper;
    @Autowired
    ZoneSpuMapper zoneSpuMapper;
    @Autowired
    OSSClientUtil ossClientUtil;
    @Autowired
    SpuStatisticsMapper spuStatisticsMapper;
    @Value("${oss.bucket}")
    String bucket;
    @Autowired
    ShopCategoryMapper shopCategoryMapper;
    @Autowired
    ShareActivitySpuMapper shareActivitySpuMapper;

    /**
     * 模糊删除redis，支持通配符
     *
     * @param key
     */
    private void deleteRedisByKey(String key) {
        Set<String> keys = redisTemplate.keys(key);
        if (!CollectionUtils.isEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }

    public boolean computedIsUseCache(SpuReq req) {
        // 只缓存首页，并且没有搜索其他内容
        return req.getPageNo() == 1 && req.getAuditStatus() == null && req.getCategoryId() == null && req.getHidden() == null && req.getIsVirtual() == null && req.getName() == null && req.getBrandId() == null;
    }

    @Override
    public ResultData<BasePageResp<SpuResp>> getSpuPage(SpuReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<SpuResp> list = spuMapper.findAllAdminPage(req);
        if (CollectionUtils.isEmpty(list)) {
            return PageHelpUtil.buildPage(list, page);
        }

        List<ShopCategory> shopCategoryList = shopCategoryMapper.findAllByIdIn(list.stream().map(Spu::getScThirdId).collect(Collectors.toList()));
        Map<Long, ShopCategory> shopCategoryMap = new HashMap<>();
        shopCategoryList.stream().forEach(item -> {
            shopCategoryMap.put(item.getId(), item);
        });

        Map<Long, Category> categoryMap = daoService.getCategoryMap(list.stream().map(Spu::getCategoryId).collect(Collectors.toList()));
        Map<Long, Brand> brandMap = daoService.getBrandMap(list.stream().map(Spu::getBrandId).collect(Collectors.toList()));
        Map<Long, Freight> freightMap = daoService.getFreightMap(list.stream().map(Spu::getFreightId).collect(Collectors.toList()));
        // 是否已绑定分享赚
        List<ShareActivitySpuVo> spus = shareActivitySpuMapper.getShareActivitySpuByIds(list.stream().map(Spu::getId).collect(Collectors.toList()));
        if (!CollectionUtils.isEmpty(spus)) {
            list.forEach(l -> {
                spus.forEach(spu -> {
                    if (spu.getSpuId().equals(l.getId())) {
                        l.setBind(true);
                        l.setBindTitle(spu.getBindTitle());
                        l.setShareActivityId(spu.getShareActivityId());
                    }
                });
            });
        }
        list.stream().forEach((item) -> {
            // 防止空指针
            item.setCategoryTitle(Optional.ofNullable(categoryMap.get(item.getCategoryId())).map(Category::getTitle).orElseGet(String::new));
            item.setBrandName(brandMap.containsKey(item.getBrandId()) ? brandMap.get(item.getBrandId()).getName() : null);
            item.setFreightName(Optional.ofNullable(freightMap.get(item.getFreightId())).map(Freight::getName).orElseGet(String::new));
            if (item.getScThirdId() != null) {
                ShopCategory shopCategory = shopCategoryMap.get(item.getScThirdId());
                if (shopCategory != null) {
                    item.setScThirdTitle(shopCategory.getTitle());
                }
            }
        });

        ResultData<BasePageResp<SpuResp>> result = PageHelpUtil.buildPage(list, page);
        return result;
    }

    @Override
    public ResultData<BasePageResp<SpuResp>> getProductPage(SpuReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        boolean isUseCache = computedIsUseCache(req);
        ValueOperations<String, ResultData<BasePageResp<SpuResp>>> operations = redisTemplate.opsForValue();
        String key = "MerchantProductPage:" + merchant.getTenantId() + ":" + DigestUtils.md5DigestAsHex((JSON.toJSONString(req)).getBytes());
        if (isUseCache) {
            ResultData<BasePageResp<SpuResp>> result = operations.get(key);
            if (result != null) {
                return result;
            }
        }
        ResultData<BasePageResp<SpuResp>> result = getSpuPage(req);
        if (CollectionUtils.isEmpty(result.getValue().getData())) {
            return result;
        }
        // 获取skuList
        Map<Long, List<Sku>> skuListMap = daoService.getSkuListMap(result.getValue().getData().stream().map(SpuResp::getId).collect(Collectors.toList()));
        result.getValue().getData().stream().forEach(item -> item.setSkuList(skuListMap.get(item.getId())));
        if (isUseCache) {
            operations.set(key, result, 10, TimeUnit.MINUTES);
        }
        return result;
    }

    /**
     * 创建商品SPU级快照
     * 0.0.1: 初始版本
     *
     * @param spu
     * @param spuAttributes
     */
    private void createSnapshot(Spu spu, List<SpuAttribute> spuAttributes) {
        SnapshotDTO snapshotDTO = new SnapshotDTO(spu, spuAttributes);
        Snapshot snapshot = Snapshot.builder()
                .spuId(spu.getId())
                .version("0.0.1")
                .content(JSON.toJSONString(snapshotDTO))
                .createdAt(new Date())
                .build();
        snapshotMapper.insert2(snapshot);
    }

    private static String getImagePrefix(String imageUrl) {
        String[] split = imageUrl.split("\\/");
        return split[2];
    }

    private void replaceImage(Spu spu, List<Sku> skuList, Long tenantId) {
        String mainImage = spu.getMainImage();
        if (!bucket.equals(getImagePrefix(mainImage))) {
            List<String> imageList = new ArrayList<>();
            String content = spu.getContent();
            String spuImageList = spu.getImageList();
            List<String> skuImages = skuList.stream().map(Sku::getImage).collect(Collectors.toList());
            imageList.addAll(Arrays.asList(content.split(",")));
            imageList.add(mainImage);
            imageList.addAll(Arrays.asList(spuImageList.split(",")));
            imageList.addAll(skuImages);

            List<ReplaceImage> addReplaceImage = new ArrayList<>();
            List<ReplaceImage> replaceImages = replaceImageMapper.findAllByFromUrlIn(imageList);
            Map<String, String> replaceImageMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(replaceImages)) {
                replaceImages.stream().forEach(item -> {
                    replaceImageMap.put(item.getFromUrl(), item.getToUrl());
                });
            }

            imageList.stream().forEach(item -> {
                if (!replaceImageMap.containsKey(item)) {
                    try {
                        String toUrl = ossClientUtil.uploadWebImage(item);
                        addReplaceImage.add(ReplaceImage.builder()
                                .fromUrl(item)
                                .toUrl(toUrl)
                                .tenantId(tenantId)
                                .build());
                        replaceImageMap.put(item, toUrl);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            if (!CollectionUtils.isEmpty(addReplaceImage)) {
                replaceImageMapper.batchInsert(addReplaceImage);
            }

            for (String key : replaceImageMap.keySet()) {
                String value = replaceImageMap.get(key);
                content = content.replace(key, value);
                spuImageList = spuImageList.replace(key, value);
                mainImage = mainImage.replace(key, value);
            }
            skuList.stream().forEach(item -> {
                item.setImage(replaceImageMap.get(item.getImage()));
            });
            spu.setContent(content);
            spu.setImageList(spuImageList);
            spu.setMainImage(mainImage);

        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResp editSpuAndSku(SpuEditVO spuEditVO) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        Spu spu = spuEditVO.getSpu();
        List<Sku> skuList = spuEditVO.getSkuList();
        List<SpuAttribute> spuAttributeList = spuEditVO.getSpuAttributeList();

        //图片转存
        replaceImage(spu, skuList, merchant.getTenantId());

        Shop shop = shopMapper.selectByPrimaryKey(merchant.getTenantId());
        Category category = categoryMapper.selectByPrimaryKey(spu.getCategoryId());

        spu.setShopId(shop.getId());
        spu.setLatitude(shop.getLatitude());
        spu.setLongitude(shop.getLongitude());
        spu.setIndustryFirstId(shop.getIndustryFirstId());
        spu.setIndustrySecondId(shop.getIndustrySecondId());

        // 批量设置供货价
        for (Sku item : skuList) {
            BigDecimal costPrice = item.getPrice().multiply(BigDecimal.ONE.subtract(shop.getServiceRate())).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            if (item.getCostPrice() == null || item.getCostPrice().compareTo(costPrice) > 0) {
                item.setCostPrice(costPrice);
            }
            // 如果没设置，则设置默认值
            if (item.getChangePrice() == null || item.getChangePrice().compareTo(BigDecimal.ZERO) <= 0) {
                item.setChangePrice(costPrice);
            }
            item.setType(spu.getType());
        }


        if (ShopTypeEnum.BUSINESS.getType().equals(shop.getType())) {
            spu.setType(SpuTypeEnum.SHOP.getType());
            // fixme 目前先让所有商家的商品都为预约商品
            if (spu.getShopSpuType() == null) {
                spu.setShopSpuType(ShopSpuTypeEnum.APPOINTMENT.getType());
            }
        }

        // 如果主播佣金比例大于30%，则不允许
        if (spu.getAnchorProportion().compareTo(BigDecimal.valueOf(0.3)) > 0) {
            return new BaseResp(ResultCode.SPU_COMMISSION_ERROR);
        }

        if (SpuTypeEnum.NORMAL.getType().equals(spu.getType()) || SpuTypeEnum.EXCHANGE.getType().equals(spu.getType()) || SpuTypeEnum.COMMISSION.getType().equals(spu.getType())) {
            if (spu.getId() == null) {
                if (SpuHiddenEnum.VISIBLE.getType().equals(spu.getHidden())) {
                    // 如果是想上架，则要进入待审核
                    spu.setHidden(SpuHiddenEnum.WAIT_AUDIT.getType());
                }
                spu.setAuditStatus(SpuAuditStatusEnum.WAIT_AUDIT.getAuditStatus());
            }
        } else {
            spu.setAuditStatus(SpuAuditStatusEnum.PASS.getAuditStatus());
        }

        if (spuEditVO.getSpu().getId() == null) {
            // 新建
            spu.setDeleted((byte) 0);
            spu.setSales(0);
            spu.setFiveStar(0);
            spu.setSorts(0);
            spu.setCreatedAt(new Date());
            spu.setUpdatedAt(new Date());
            spu.setRid(category.getRid() != null ? category.getRid() : category.getId());
            spu.setTenantId(merchant.getTenantId());
            if (spu.getLimitCount() == null) {
                spu.setLimitCount(0L);
            }
            if (spu.getFlashHidden() == null) {
                spu.setFlashHidden(ToggleEnum.ON.getStatus());
            }
            spuMapper.insert(spu);

            for (Sku sku : skuList) {
                sku.setSpuId(spu.getId());
                sku.setSales(0);
                sku.setDeleted((byte) 0);
                sku.setCreatedAt(new Date());
                sku.setUpdatedAt(new Date());
                sku.setChangePrice(new BigDecimal(BigInteger.ZERO));
                if (sku.getPartnerPrice() == null) {
                    sku.setPartnerPrice(sku.getPrice());
                }
            }
            skuMapper.batchInsert2(skuList);

            if (spuAttributeList != null && spuAttributeList.size() > 0) {
                spuAttributeMapper.batchInsert(spuAttributeList, spu.getId());
            }

            // 初始化spu评价统计
            commentStatisticsMapper.insert(new CommentStatistics(null, spu.getId(), 0, 0, 0, 0, 0, 0, 0, 0));
        } else {
            // 编辑
            spu.setUpdatedAt(new Date());
            spu.setTenantId(spu.getShopId());
            spuMapper.updateByPrimaryKeySelective(spu);

            if (spuEditVO.getIsSkuChange()) {
                List<Sku> skuResult = skuMapper.findAllBySpuId(spu.getId());
                Map<String, Sku> skuMap = new HashMap<>();
                skuResult.stream().forEach(item -> skuMap.put(item.getSpecValues(), item));

                // 拆分出删除、插入、更新
                List<Sku> skuDelete = new ArrayList<>();
                List<Sku> skuInsert = new ArrayList<>();
                List<Sku> skuUpdate = new ArrayList<>();

                Map<String, Sku> newSkuMap = new HashMap<>();
                skuList.stream().forEach(item -> {
                    if (skuMap.containsKey(item.getSpecValues())) {
                        item.setUpdatedAt(new Date());
                        skuUpdate.add(item);
                    } else {
                        item.setSpuId(spu.getId());
                        item.setSales(0);
                        item.setDeleted((byte) 0);
                        item.setCreatedAt(new Date());
                        item.setUpdatedAt(new Date());
                        skuInsert.add(item);
                    }
                    newSkuMap.put(item.getSpecValues(), item);
                });
                skuResult.forEach(item -> {
                    item.setTenantId(spu.getTenantId());
                    if (!newSkuMap.containsKey(item.getSpecValues())) {
                        skuDelete.add(item);
                    }
                });
                // 先删除，再插入，再更新
                if (!CollectionUtils.isEmpty(skuDelete)) {
                    skuMapper.updateDeletedByIdIn(DeletedEnum.DELETED.getStatus(), skuDelete.stream().map(Sku::getId).collect(Collectors.toList()));
                }
                if (!CollectionUtils.isEmpty(skuInsert)) {
                    skuMapper.batchInsert2(skuInsert);
                }
                if (!CollectionUtils.isEmpty(skuUpdate)) {
                    skuMapper.updateBatchSelective(skuUpdate);
                }
            } else {
                // 如果sku只是修改了价格数量，则只更新
                for (Sku sku : skuList) {
                    sku.setUpdatedAt(new Date());
                }
                skuMapper.updateBatchSelective(skuList);
            }

            spuAttributeMapper.deleteBySpuId(spu.getId());
            if (spuAttributeList != null && spuAttributeList.size() > 0) {
                spuAttributeMapper.batchInsert(spuAttributeList, spu.getId());
            }
        }

        // 创建快照
        createSnapshot(spu, spuAttributeList);

        // 删除管理后台缓存
        deleteRedisByKey("AdminProductPage*");
        deleteRedisByKey("HomeInfoResp");
        deleteRedisByKey("AdminSpuPage*");
        deleteRedisByKey("MerchantProductPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("SpuPage*");
        deleteRedisByKey("ProductDetail:" + spu.getId() + ":*");
        deleteRedisByKey("SkuList:" + spu.getId() + ":*");
        deleteRedisByKey("LikeSpu:" + spu.getCategoryId());

        //重新统计并保存数据
        Spu existSpu = spuMapper.selectByPrimaryKey(spu.getId());
        saveSpuStatistics(spuEditVO.getSpu(), existSpu.getCategoryId());

        return new BaseResp();
    }

    private void saveSpuStatistics(Spu spu, Long categoryId) {
        List<Long> categoryList = new ArrayList<>();
        categoryList.add(categoryId);
        saveSpuStatistics(spu, categoryList);
    }

    @Override
    public ResultData<SpuEditVO> getSpuEditDetail(Long id) {
        SpuEditVO spuEditVO = new SpuEditVO();
        Spu spu = spuMapper.selectByPrimaryKey(id);
        Category category = categoryMapper.selectByPrimaryKey(spu.getCategoryId());
        List<Category> categoryList = new ArrayList<>();
        if (category.getLevel().equals(2)) {
            Category parentCategory = categoryMapper.selectByPrimaryKey(category.getPid());
            Category rootCategory = categoryMapper.selectByPrimaryKey(category.getRid());
            categoryList.add(rootCategory);
            categoryList.add(parentCategory);
            categoryList.add(category);
        } else if (category.getLevel().equals(1)) {
            Category parentCategory = categoryMapper.selectByPrimaryKey(category.getPid());
            categoryList.add(parentCategory);
            categoryList.add(category);
        } else {
            categoryList.add(category);
        }

        spuEditVO.setCategoryList(categoryList);
        spuEditVO.setSpu(spu);
        spuEditVO.setSkuList(skuMapper.findAllBySpuId(id));
        spuEditVO.setSpuAttributeList(spuAttributeMapper.findAllBySpuId(id));
        return new ResultData<>(spuEditVO);
    }

    @Override
    public ResultData<BasePageResp<Spec>> getSpecPage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Spec> list = specMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<List<Spec>> getSpecList() {
        return new ResultData<>(specMapper.findAll());
    }

    @Override
    public ResultData<BasePageResp<Unit>> getUnitPage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Unit> list = unitMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<List<Unit>> getUnitList() {
        return new ResultData<>(unitMapper.findAll());
    }

    @Override
    public ResultData<BasePageResp<Freight>> getFreightPage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Freight> list = freightMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public BaseResp editFreight(FreightVO freightVO) {
        Freight freight = freightVO.getFreight();
        if (freight.getFirstWeight().compareTo(MAX_AMOUNT) > 0 || freight.getAdditionalWeight().compareTo(MAX_AMOUNT) > 0) {
            return new BaseResp(ResultCode.OUT_OF_AMOUNT_RANGE);
        }
        List<FreightList> freightLists = freightVO.getFreightLists();
        Boolean overMax = false;
        for (int i = 0; i < freightLists.size(); i++) {
            if (freightLists.get(i).getFirstWeight().compareTo(MAX_AMOUNT) > 0 || freightLists.get(i).getAdditionalWeight().compareTo(MAX_AMOUNT) > 0) {
                overMax = true;
                break;
            }
        }
        if (overMax) {
            return new BaseResp(ResultCode.OUT_OF_AMOUNT_RANGE);
        }
        if (freight.getId() == null) {
            freight.setDeleted((byte) 0);
            freight.setCreatedAt(new Date());
            freight.setUpdatedAt(new Date());
            freightMapper.insert2(freight);
        } else {
            freight.setUpdatedAt(new Date());
            freightMapper.updateByPrimaryKeySelective(freight);
            freightListMapper.deleteByFreightId(freight.getId());
        }

        for (FreightList item : freightLists) {
            item.setFreightId(freight.getId());
        }
        if (!CollectionUtils.isEmpty(freightLists)) {
            freightListMapper.batchInsert2(freightLists);
        }
        return new BaseResp();
    }

    @Override
    public ResultData<FreightVO> getFreightDetail(Long id) {
        FreightVO freightVO = new FreightVO();
        freightVO.setFreight(freightMapper.selectByPrimaryKey(id));
        freightVO.setFreightLists(freightListMapper.findAllByFreightId(id));
        return new ResultData<>(freightVO);
    }

    @Override
    public BaseResp deleteFreight(Long id) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Long count = freightMapper.countByTenantIdAndDeleted(merchant.getTenantId(), DeletedEnum.DEFAULT.getStatus());
        if (count == null && count.equals(0L)) {
            return new BaseResp(ResultCode.FREIGHT_CAN_NOT_BE_EMPTY);
        }
        freightMapper.updateDeletedById((byte) 1, id);
        return new BaseResp();
    }

    @Override
    public ResultData<List<Freight>> getFreightList() {
        return new ResultData<>(freightMapper.findAll());
    }

    @Override
    public BaseResp editSpuCommission(SpuCommission spuCommission) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        SpuCommission result = spuCommissionMapper.findOneBySpuId(spuCommission.getSpuId());

        // 不允许佣金总和大于商户拥有的比例
        // 实际发放处依旧要再次检测，以防设置佣金之后，此厂家又修改了商品价格，反向赚取平台费用。
        BigDecimal commission = result.getAnchorCommissionRate().add(result.getRecommendCommissionRate());
        List<Sku> skuList = skuMapper.findAllBySpuId(spuCommission.getSpuId());

        BaseResp baseResp = null;
        for (Sku sku : skuList) {
            if (sku.getCostPrice().divide(sku.getPrice()).compareTo(commission) < 0) {
                baseResp = new BaseResp(ResultCode.SPU_COMMISSION_ERROR);
            }
        }
        if (baseResp != null) {
            return baseResp;
        }

        if (result == null) {
            spuCommission.setCreatedAt(new Date());
            spuCommission.setUpdatedAt(new Date());
            spuCommission.setTenantId(merchant.getTenantId());
            spuCommissionMapper.insert(spuCommission);
        } else {
            result.setUpdatedAt(new Date());
            spuCommissionMapper.updateByPrimaryKeySelective(result);
        }
        // 删除管理后台缓存
        deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
        return new BaseResp();
    }

    @Override
    public BaseResp deleteProduct(Long id) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        //更新商品统计数据
        Spu spu = spuMapper.selectByPrimaryKey(id);
        saveSpuStatistics(null, spu.getCategoryId());

        spuMapper.updateDeletedById(DeletedEnum.DELETED.getStatus(), id);
        skuMapper.updateDeletedBySpuId(DeletedEnum.DELETED.getStatus(), id);

        //如果是预约商品删除设置
        if (ShopSpuTypeEnum.APPOINTMENT.getType().equals(spu.getShopSpuType())) {
            appointmentSettingMapper.updateDeletedBySpuId(DeletedEnum.DELETED.getStatus(), spu.getId());
        }

        // 删除管理后台缓存
        deleteRedisByKey("MerchantProductPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("SpuPage*");

        return new BaseResp();
    }

    @Override
    public BaseResp editProductHidden(Long id) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        Spu spu = spuMapper.selectByPrimaryKey(id);
        if (HiddenEnum.PLATFORM_HIDDEN.getRemark().equals(spu.getHidden())) {
            return new BaseResp(ResultCode.PLATFORM_HIDDEN);
        }
        if (HiddenEnum.VISIBLE.getStatus().equals(spu.getHidden())) {
            spuMapper.updateHiddenById(HiddenEnum.HIDDEN.getStatus(), id);
            skuMapper.updateHiddenBySpuId(HiddenEnum.HIDDEN.getStatus(), id);
        }
        if (HiddenEnum.HIDDEN.getStatus().equals(spu.getHidden())) {
            spuMapper.updateHiddenById(HiddenEnum.VISIBLE.getStatus(), id);
            skuMapper.updateHiddenBySpuId(HiddenEnum.VISIBLE.getStatus(), id);
        }

        //更新商品统计数据
        saveSpuStatistics(null, spu.getCategoryId());

        // 删除管理后台缓存
        deleteRedisByKey("MerchantProductPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("SpuPage*");
        return new BaseResp();
    }

    @Override
    public BaseResp batchOfflineProduct(BatchOfflineReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        List<Long> spuIds = req.getSpuIds();
        if (!CollectionUtils.isEmpty(spuIds)) {
            spuMapper.batchOffline(spuIds, req.getHidden());
            skuMapper.batchOffline(spuIds, req.getHidden());

            //下架商品更新统计数据
            List<Spu> spuList = spuMapper.findAllByIds(spuIds);
            if (CollectionUtils.isEmpty(spuList)) {
                return new BaseResp(ResultCode.NOT_YOUR_MERCHANT);
            }
            saveSpuStatistics(null, spuList.stream().map(Spu::getCategoryId).collect(Collectors.toList()));


            // 删除管理后台缓存
            deleteRedisByKey("MerchantProductPage:" + merchant.getTenantId() + ":*");
            deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
            deleteRedisByKey("SpuPage*");
        }

        return new BaseResp();
    }

    private void saveSpuStatistics(Spu spu, List<Long> categoryIds) {
        //添加删除或修改了商品名称就更新统计数据
        ValueOperations<String, List<TagSpuStatisticsVO>> operations = redisTemplate.opsForValue();
        if (spu == null || spu.getName() != null) {
            //标题总字数
            List<StatisticsCountVO> totalWordCounts = spuMapper.getTotalWordCountByCategoryIds(categoryIds);
            Map<Long, Long> totalWordCountMap = new HashMap<>();
            if (!org.apache.commons.collections.CollectionUtils.isEmpty(totalWordCounts)) {
                totalWordCounts.stream().forEach(item -> {
                    totalWordCountMap.put(item.getId(), item.getCount());
                });
            }

            //博文总数
            List<StatisticsCountVO> itemCount = spuMapper.getItemCountByCategoryIds(categoryIds);
            Map<Long, Long> itemCountMap = new HashMap<>();
            if (!org.apache.commons.collections.CollectionUtils.isEmpty(itemCount)) {
                itemCount.stream().forEach(item -> {
                    itemCountMap.put(item.getId(), item.getCount());
                });
            }

            //计算统计数量
            List<TagSpuStatisticsVO> voList = new ArrayList<>();
            totalWordCounts.stream().forEach(item -> {
                TagSpuStatisticsVO vo = new TagSpuStatisticsVO();
                vo.setType(UserTagTypeEnum.SPU.getType());
                vo.setRelateId(item.getId());
                vo.setItemCount(itemCountMap.get(item.getId()));
                vo.setUpdateAt(new Date());
                vo.setTotalWordCount(totalWordCountMap.get(item.getId()));
                voList.add(vo);
            });

            if (operations.get("tfidf" + UserTagTypeEnum.SPU.getType()) != null) {
                //更新下架的类目统计
                List<TagSpuStatisticsVO> statisticsVO = operations.get("tfidf" + UserTagTypeEnum.SPU.getType());
                statisticsVO = statisticsVO.stream().filter(Objects::nonNull).collect(Collectors.toList());
                List<Long> relateIds = statisticsVO.stream().map(TagSpuStatisticsVO::getRelateId).collect(Collectors.toList());
                List<TagSpuStatisticsVO> addStatistics = new ArrayList<>();

                Map<Long, TagSpuStatisticsVO> statisticsVOMap = new HashMap<>();
                statisticsVO.stream().forEach(item -> {
                    statisticsVOMap.put(item.getRelateId(), item);
                });

                voList.stream().forEach(item -> {
                    if (relateIds.contains(item.getRelateId())) {
                        BeanUtils.copyProperties(item, statisticsVOMap.get(item.getRelateId()));
                    } else {
                        addStatistics.add(item);
                    }
                });
                if (!CollectionUtils.isEmpty(addStatistics)) {
                    statisticsVO.addAll(addStatistics);
                }
                operations.set("tfidf" + UserTagTypeEnum.SPU.getType(), statisticsVO);
            }
        }
    }

    @Override
    public BaseResp resetSorts(Long startNum, Long endNum) {
        spuMapper.resetSorts(startNum, endNum);
        return new BaseResp();
    }

    @Override
    public BaseResp editChangePrice(EditChangePriceReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Spu spu = spuMapper.selectByPrimaryKey(req.getSpuId());
        if (spu.getTenantId().equals(merchant.getTenantId())) {
            List<Sku> skuList = req.getSkuList();
            if (!CollectionUtils.isEmpty(skuList)) {
                spuMapper.updateAuditStatusAndAuditMessageById(SpuAuditStatusEnum.WAIT_AUDIT.getAuditStatus(), null, req.getSpuId());
                skuMapper.updateBatchChangePrice(skuList);
            }
        }
        return new BaseResp();
    }

    @Override
    public ResultData<ShopStatisticsDataVO> getShopStatisticsData(TimeRangeReq req) {
        ShopStatisticsDataVO vo = new ShopStatisticsDataVO();
        OrderStatisticAmountVO amountVO = orderSkuMapper.getShopStatisticsData(req);
        vo.setAmount(amountVO.getOrderAmount());
        vo.setOrderCount(amountVO.getOrderCount());
        vo.setCount(orderSkuMapper.getUserCount(req));
        vo.setRefundAmount(afterSaleMapper.getShopStatisticsData(req));
        SpuStatisticsSumVO shopStatisticsData = spuStatisticsMapper.getShopStatisticsData(req);
        vo.setPv(shopStatisticsData.getPv());
        vo.setUv(shopStatisticsData.getUv());
        return new ResultData<>(vo);
    }


    /**
     * 小时内数据为空 塞入0
     *
     * @param req
     * @param voList
     * @return
     */
    private List<StatisticsVO> supplementHour(TimeRangeReq req, List<StatisticsVO> voList) {
        List<String> hours = voList.stream().map(StatisticsVO::getDate).collect(Collectors.toList());
        SimpleDateFormat hourFormat = new SimpleDateFormat("HH");
        Integer startHour = Integer.valueOf(hourFormat.format(req.getStartTime()));
        Integer endHour = Integer.valueOf(hourFormat.format(req.getEndTime()));
        for (Integer i = startHour; i <= endHour; i++) {
            if (!hours.contains(i.toString())) {
                voList.add(StatisticsVO.builder()
                        .date(i.toString())
                        .statisticsMoney(BigDecimal.ZERO)
                        .statisticsNumber(0)
                        .build());
            }
        }
        Collections.sort(voList, Comparator.comparingInt(o -> Integer.valueOf(o.getDate())));
        return voList;
    }


    @Override
    public ResultData<SpuTrendVO> getSpuClickTrend(TimeRangeReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SpuTrendVO vo = new SpuTrendVO();
        req.setEndTime(new Date());
        List<StatisticsVO> todayStatistics = spuStatisticsMapper.getSpuClickTrend(req, merchant.getTenantId());
        supplementHour(req, todayStatistics);
        vo.setTodayStatistics(todayStatistics);
        List<StatisticsVO> yesterdayStatistics = spuStatisticsMapper.getSpuClickTrend(getYesterdayRange(req), merchant.getTenantId());
        supplementHour(req, yesterdayStatistics);
        vo.setYesterdayStatistics(yesterdayStatistics);
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<SpuTrendVO> getSpuVisitorTrend(TimeRangeReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SpuTrendVO vo = new SpuTrendVO();
        req.setEndTime(new Date());
        List<StatisticsVO> todayStatistics = spuStatisticsMapper.getSpuVisitorTrend(req, merchant.getTenantId());
        supplementHour(req, todayStatistics);
        vo.setTodayStatistics(todayStatistics);
        List<StatisticsVO> yesterdayStatistics = spuStatisticsMapper.getSpuVisitorTrend(getYesterdayRange(req), merchant.getTenantId());
        supplementHour(req, yesterdayStatistics);
        vo.setYesterdayStatistics(yesterdayStatistics);
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<SpuTrendVO> getSpuRefundCountTrend(TimeRangeReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SpuTrendVO vo = new SpuTrendVO();
        req.setEndTime(new Date());
        List<StatisticsVO> todayStatistics = afterSaleMapper.getSpuRefundTrend(req, merchant.getTenantId());
        supplementHour(req, todayStatistics);
        vo.setTodayStatistics(todayStatistics);
        List<StatisticsVO> yesterdayStatistics = afterSaleMapper.getSpuRefundTrend(getYesterdayRange(req), merchant.getTenantId());
        supplementHour(req, yesterdayStatistics);
        vo.setYesterdayStatistics(yesterdayStatistics);
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<SpuTrendVO> getSpuCompleteTrend(TimeRangeReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SpuTrendVO vo = new SpuTrendVO();
        req.setEndTime(new Date());
        List<StatisticsVO> todayStatistics = orderSkuMapper.getSpuCompleteTrend(req, merchant.getTenantId());
        supplementHour(req, todayStatistics);
        vo.setTodayStatistics(todayStatistics);
        List<StatisticsVO> yesterdayStatistics = orderSkuMapper.getSpuCompleteTrend(getYesterdayRange(req), merchant.getTenantId());
        supplementHour(req, yesterdayStatistics);
        vo.setYesterdayStatistics(yesterdayStatistics);
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<SpuTrendVO> getSpuCustomerTrend(TimeRangeReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        SpuTrendVO vo = new SpuTrendVO();
        req.setEndTime(new Date());
        List<StatisticsVO> todayStatistics = orderSkuMapper.getSpuCustomerTrend(req, merchant.getTenantId());
        supplementHour(req, todayStatistics);
        vo.setTodayStatistics(todayStatistics);
        List<StatisticsVO> yesterdayStatistics = orderSkuMapper.getSpuCustomerTrend(getYesterdayRange(req), merchant.getTenantId());
        supplementHour(req, yesterdayStatistics);
        vo.setYesterdayStatistics(yesterdayStatistics);
        return new ResultData<>(vo);
    }

    private TimeRangeReq getYesterdayRange(TimeRangeReq req) {
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.setTime(req.getStartTime());
        startCalendar.add(Calendar.DATE, -1);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(req.getEndTime());
        endCalendar.add(Calendar.DATE, -1);
        req.setStartTime(startCalendar.getTime());
        req.setEndTime(endCalendar.getTime());
        return req;
    }

    private TimeRangeReq getTodayRange(TimeRangeReq req) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        req.setStartTime(getDateStart(calendar));
        req.setEndTime(getDateEnd(calendar));
        return req;
    }

    public Date getDateStart(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    public Date getDateEnd(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    @Override
    public BaseResp editZone(ZoneReq req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (req.getId() == null) {
            Zone zone = Zone.builder()
                    .title(req.getTitle())
                    .mainImage(req.getMainImage())
                    .backgroundColor(req.getBackgroundColor())
                    .deleted(DeletedEnum.DEFAULT.getStatus())
                    .createdAt(new Date())
                    .updatedAt(new Date())
                    .build();
            if (req.getNewZone() == null) {
                zone.setNewZone(ToggleEnum.OFF.getStatus());
            }
            zoneMapper.insert2(zone);

            if (!CollectionUtils.isEmpty(req.getList())) {
                Map<Long, Spu> spuMap = daoService.getSpuMap(req.getList().stream().map(ZoneSpu::getSpuId).collect(Collectors.toList()));
                List<ZoneSpu> zoneSpuList = new ArrayList<>();
                req.getList().stream().forEach(item -> {
                    if (spuMap.get(item.getSpuId()).getTenantId().equals(merchant.getTenantId())) {
                        ZoneSpu zoneSpu = new ZoneSpu();
                        BeanUtils.copyProperties(item, zoneSpu);
                        zoneSpu.setZoneId(zone.getId());
                        zoneSpu.setCreatedAt(new Date());
                        zoneSpu.setUpdatedAt(new Date());
                        zoneSpu.setNewZone(zone.getNewZone());
                        zoneSpuList.add(zoneSpu);
                    }
                });
                if (!CollectionUtils.isEmpty(zoneSpuList)) {
                    zoneSpuMapper.batchInsert(zoneSpuList);
                }
            }
        } else {
            Zone zone = zoneMapper.selectByPrimaryKey(req.getId());
            zoneMapper.updateByPrimaryKeySelective(req);
            zoneSpuMapper.deleteByZoneId(req.getId());
            List<ZoneSpu> list = req.getList();
            if (!CollectionUtils.isEmpty(req.getList())) {
                Map<Long, Spu> spuMap = daoService.getSpuMap(list.stream().map(ZoneSpu::getSpuId).collect(Collectors.toList()));
                req.getList().stream().forEach(item -> {
                    if (spuMap.get(item.getSpuId()).getTenantId().equals(merchant.getTenantId())) {
                        item.setZoneId(req.getId());
                        item.setNewZone(req.getNewZone() == null ? zone.getNewZone() : req.getNewZone());
                        item.setCreatedAt(new Date());
                        item.setUpdatedAt(new Date());
                    }
                });
                zoneSpuMapper.batchInsert(req.getList());
            }
        }
        redisTemplate.delete("HomeInfoResp");
        return new BaseResp();
    }

    @Override
    public ResultData<BasePageResp<Zone>> getZonePage(ZonePageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Zone> list = zoneMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<ZoneVO> getZoneSpuDetail(Long zoneId) {
        ZoneVO vo = new ZoneVO();
        Zone zone = zoneMapper.selectByPrimaryKey(zoneId);
        if (zone != null) {
            BeanUtils.copyProperties(zone, vo);
            List<ZoneSpuVO> zoneSpuVOList = zoneSpuMapper.findAllByZoneId(zone.getId());
            if (!CollectionUtils.isEmpty(zoneSpuVOList)) {
                Map<Long, Spu> spuMap = daoService.getSpuMap(zoneSpuVOList.stream().map(ZoneSpuVO::getSpuId).collect(Collectors.toList()));
                zoneSpuVOList.stream().forEach(item -> {
                    item.setName(spuMap.get(item.getSpuId()).getName());
                    item.setImage(spuMap.get(item.getSpuId()).getMainImage());
                    item.setPrice(spuMap.get(item.getSpuId()).getMinPrice());
                    item.setFeature(spuMap.get(item.getSpuId()).getFeature());
                    item.setSales(spuMap.get(item.getSpuId()).getSales());
                });
            }
            vo.setZoneSpuVOList(zoneSpuVOList);
        }
        return new ResultData<>(vo);
    }

    @Override
    public BaseResp deleteZone(Long zoneId) {
        zoneMapper.updateDeletedById(DeletedEnum.DELETED.getStatus(), zoneId);
        return new BaseResp();
    }

    @Override
    public BaseResp editSpuShopCategory(Long spuId, Long scThirdId) {
        ShopCategory shopCategory = shopCategoryMapper.selectByPrimaryKey(scThirdId);
        if (shopCategory != null) {
            Spu spu = Spu.builder()
                    .id(spuId)
                    .scFirstId(shopCategory.getRid())
                    .scSecondId(shopCategory.getPid())
                    .scThirdId(shopCategory.getId())
                    .build();
            spuMapper.updateByPrimaryKeySelective(spu);
        }
        return new BaseResp();
    }

    @Override
    public BaseResp editBatchSpuShopCategory(List<EditSpuShopCategoryReq> req) {
        Merchant merchant = (Merchant) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!CollectionUtils.isEmpty(req)) {
            List<ShopCategory> shopCategories = shopCategoryMapper.findAllByIdInAndTenantId(req.stream().map(EditSpuShopCategoryReq::getScThirdId).collect(Collectors.toList()), merchant.getTenantId());
            Map<Long, ShopCategory> shopCategoryMap = new HashMap<>();
            shopCategories.stream().forEach(item -> {
                shopCategoryMap.put(item.getId(), item);
            });
            req.stream().forEach(item -> {
                ShopCategory shopCategory = shopCategoryMap.get(item.getScThirdId());
                item.setScSecondId(shopCategory.getPid());
                item.setScFirstId(shopCategory.getRid());
            });
            spuMapper.batchUpdateShopCategory(req);
        }
        // 删除管理后台缓存
        deleteRedisByKey("MerchantProductPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("MerchantSpuPage:" + merchant.getTenantId() + ":*");
        deleteRedisByKey("SpuPage*");
        return new BaseResp();
    }

    @Override
    public ResultData<BasePageResp<Sku>> getSkuList(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Sku> skus = skuMapper.findAllPage(req);
        return PageHelpUtil.buildPage(skus, page);
    }
}